

import Config from './config.json';

class Utils {
    /**
     * 从default_properties来扩展new_properties的属性。new_properties中不存在的属性或key，会从default_properties里取默认值。<br/>
     * 如果allow_other设置成true，允许使用default_properties字典里没有的属性。
     *
     * @function Utils.extend
     *
     * @param {Object} new_properties - 要扩展的字典。
     * @param {Object} default_properties- 默认字典，用于扩展new_properties变量中没有设置的属性。
     * @param {boolean=} allow_other - 是否允许new_properties字典里存在default_properties没有的属性。默认: false
     * @return  {Object} - 扩展后的字典。
     */
    static extend(new_properties, default_properties, allow_other) {
        let out = {};

        for (var prop in new_properties) {
            if (!(allow_other || default_properties.hasOwnProperty(prop))) {
                console.error(`[ Utils.extend ] can not assign prop: ${prop}. not allowed by default value.`, new_properties, default_properties);
                continue;
            } else {
                out[prop] = new_properties[prop];
            }
        }

        for (var prop in default_properties) {
            if (!out.hasOwnProperty(prop)) {
                out[prop] = default_properties[prop];
            }
        }

        return out;
    }

    /**
     * 判断当前浏览器是否支持HTML5模式。<br/>
     * 如果在初始化ChiVoxSDK的时候没有指定mode参数，会自动根据这个函数返回值来决定是否自动使用HTML5模式。
     *
     * @function Utils.support_h5
     * @return  {boolean} - 当前浏览器是否支持HTML5模式。
     *  else if (agent.indexOf("safari") > 0 && agent.indexOf("chrome") < 0) {
     *       console.error(`[ Utils.support_h5 ] Can not running HTML5 mode on Safari. userAgent: ${agent}`);
     *  }
     *    || agent.indexOf("edge") > 0
     */

    static support_h5() {
        let supported = false;
        navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia ||  (navigator.mediaDevices && navigator.mediaDevices.getUserMedia));
        let agent = navigator.userAgent.toLowerCase();
        let AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;

        if (agent.indexOf("msie") > 0 ) {
            console.error(`[ Utils.support_h5 ] Can not running HTML5 mode on Internet Explorer. userAgent: ${agent}`);
        }else if (!navigator.getUserMedia&&!navigator.mediaDevices.getUserMedia) {
            console.error(`[ Utils.support_h5 ] Your Browser does not support getUserMedia. current: ${navigator.getUserMedia}`);
        } else if (!window.WebSocket) {
            console.error('[ Utils.support_h5 ] Your Browser does not support window.WebSocket.');
        } else if (document.location.protocol !== 'https:') {
            console.error(`[ Utils.support_h5 ] The current address: ${document.location.href}, it not based on HTTPS.`);
        } else if (!window.Worker) {
            console.error('[ Utils.support_h5 ] Your Browser does not support window.Worker.');
        } else if (!AudioContext) {
            console.error('[ Utils.support_h5 ] Your Browser does not support window.AudioContext.');
        } else {
            supported = true;
        }
        return supported;
    }

    /**
     * 使用AJAX发起一个异步HTTP请求，支持GET和POST两种方式， 返回一个Promise。<br/>
     *
     * @function Utils.ajax
     *
     * @param {Object} options - 根据options发起本次HTTP请求。默认的options属性如下：
     * @param {string=} options.type - HTTP请求的类型。允许的值有：'GET', 'POST', 默认：'GET'
     * @param {string} options.url - HTTP请求的地址。
     * @param {string=} options.querystring - HTTP请求的参数。格式：a=1&b=2
     * @param {boolean=} options.async - 是否使用XMLHttpRequest的async模式。默认：true
     * @param {string=} options.dataType - 返回值的数据类型。默认空，可以设置为'json'，返回结果会自动转换为JSON格式。
     * @return  {Object} - 返回HTTP请求的Promise。
     *
     * @example
     * // 发起http请求：
     * Utils.ajax({url: '/xxx', dataType: 'json', success: (json) => { xx; }, error: (err) => { print err; }});
     */
    static ajax(options) {
        let xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');

        let opt = {
            type: options.type || 'GET',
            url: options.url || '',
            async: options.async || false,
            dataType: options.dataType || '',
            querystring: options.querystring || '',
            success: options.success || ((json) => {}),
            error: options.error || ((err) => {})
        };

        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    const data = (opt.dataType === 'json') ? JSON.parse(xhr.responseText) : xhr.responseText;
                    opt.success(data);
                } else {
                    opt.error(new Error(xhr.status || 'Server is failed.'));
                }
            }
        };

        xhr.onerror = () => {
            opt.error(new Error(xhr.status || 'Server is failed.'));
        };
        xhr.open(opt.type, opt.url, opt.async);
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        xhr.send(opt.querystring);
    }

    /**
     * 根据当前环境， 返回一个合适的Buffer Size，HTML5模式的录音机在边录边评时使用。<br/>
     *
     * @function Utils.buff_size
     * @return  {int} - 返回当前环境合适的buffer size。
     */
    static buff_size() {
        if (/(Win(dows )?NT 6\.2)/.test(navigator.userAgent)) {
            return 1024; //Windows 8
        } else if (/(Win(dows )?NT 6\.1)/.test(navigator.userAgent)) {
            return 1024; //Windows 7
        } else if (/(Win(dows )?NT 6\.0)/.test(navigator.userAgent)) {
            return 2048; //Windows Vista
        } else if (/Win(dows )?(NT 5\.1|XP)/.test(navigator.userAgent)) {
            return 4096; //Windows XP
        } else if (/Mac|PPC/.test(navigator.userAgent)) {
            return 1024; //Mac OS X
        } else if (/Linux/.test(navigator.userAgent)) {
            return 8192; //Linux
        } else if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
            return 2048; //iOS
        } else {
            return 16384; //Otherwise
        }
    }

    /**
     * 对elements数组内所有的element绑定evname事件函数为func。<br/>
     *
     * @function Utils.bind_mul
     *
     * @param {Array} elements - 准备绑定事件的HTML DOM节点数组。
     * @param {string} evname - 要绑定的事件名称。如：'click'
     * @param {callback} func - 事件触发的函数。
     */
    static bind_mul(elements, evname, func) {
        for (var i = 0; i < elements.length; i++)
            Utils.bind(elements[i], evname, func);
    }


    /**
     * 对ele绑定evname事件函数为func。<br/>
     *
     * @function Utils.bind
     *
     * @param {Object} ele - 准备绑定事件的HTML DOM节点。
     * @param {string} evname - 要绑定的事件名称。如：'click'
     * @param {callback} func - 事件触发的函数。
     */
    static bind(ele, evname, func) {
        if (ele.addEventListener) {
            ele.addEventListener(evname, func);
        } else {
            ele.attachEvent('on' + evname, () => {
                func.call(ele);
            });
        }
    }

    /**
     * 对ele解除绑定了evname的事件函数func。<br/>
     *
     * @function Utils.unbind
     *
     * @param {Object} ele - 要解除绑定事件的HTML DOM节点。
     * @param {string} evname - 要解除绑定的事件名称。如：'click'
     * @param {callback} func - 事件触发的函数。
     */
    static unbind(ele, evname, func) {
        if (ele.removeEventListener) {
            ele.removeEventListener(evname, func);
        } else {
            ele.detachEvent('on' + evname, func);
        }
    }

    /**
     * 为ele增加CSS类。<br/>
     *
     * @function Utils.addClass
     *
     * @param {Object} ele - HTML DOM节点。
     * @param {string} classname - 要增加的CSS类名称。
     */
    static addClass(ele, classname) {
        if (ele) {
            if (ele.classList)
                ele.classList.add(classname);
            else
                ele.className += ' ' + classname;
        } else {
            console.warn('ele is null');
        }
    }

    /**
     * 移除ele的CSS类。<br/>
     *
     * @function Utils.removeClass
     *
     * @param {Object} ele - HTML DOM节点。
     * @param {string} classname - 要移除的CSS类名称。
     */
    static removeClass(ele, classname) {
        if (ele) {
            if (ele.classList)
                ele.classList.remove(classname);
            else
                ele.className = ele.className.replace(new RegExp('(^|\\b)' + classname.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
        } else {
            console.warn('ele is null');
        }
    }

    /**
     * 判断ele是否存在CSS类。<br/>
     *
     * @function Utils.hasClass
     *
     * @param {Object} ele - HTML DOM节点。
     * @param {string} classname - 要移除的CSS类名称。
     * @return {boolean} - ele是否存在classname的CSS类。
     */
    static hasClass(ele, classname) {
        if (ele.classList)
            return ele.classList.contains(classname);
        else
            return new RegExp('(^| )' + classname + '( |$)', 'gi').test(ele.className);
    }

    /**
     * 在root节点下根据selector，递归查找所有被选中的子节点。<br/>
     *
     * @function Utils.find_all
     *
     * @param {string} selector - 一个由逗号连接的包含一个或多个CSS选择器的字符串。
     * @return {Array} - 所有符合条件的子DOM节点列表。
     */
    static find_all(root, selector) {
        if (root.querySelectorAll)
            return root.querySelectorAll(selector);
    }

    /**
     * 在root节点下根据selector，递归查找第一个被选中的子节点。<br/>
     *
     * @function Utils.find
     *
     * @param {string} selector - 一个由逗号连接的包含一个或多个CSS选择器的字符串。
     * @return {Object} - 符合条件的第一个子DOM节点。
     */
    static find(root, selector) {
        var all = Utils.find_all(root, selector);

        if (all && all.length > 0) {
            return all[0];
        } else {
            Utils.log(`not found element. selector: ${selector}`);
        }
    }

    /**
     * 计算HTML DOM节点的宽度。<br/>
     *
     * @function Utils.width
     *
     * @param {Object} element - HTML DOM节点。
     * @return {int} - DOM节点的宽度。
     */
    static width(element) {
        if (element.currentStyle)
            return element.currentStyle.width;
        else
            return getComputedStyle(element, false).width;
    }

    /**
     * 生成一个UUID字符串。<br/>
     *
     * @function Utils.uuid
     * @return {string} - UUID字符串。
     */
    static uuid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
            (c) => {
                let r = Math.random() * 16 | 0,
                    v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            }
        );
    }

    /**
     * 判断item是否存在于arr数组中。<br/>
     *
     * @function Utils.in_array
     * @param {Object} item - 要判断的字符串。如果item存在于arr，返回true。
     * @param {Array} arr - 要判断的数组。
     * @return {boolean} - 是否存在。
     */
    static in_array(item, arr) {
        for (var i = 0; i < arr.length; i++) {
            if (item == arr[i]) return true;
        }

        return false;
    }

    /**
     * 在Console中打印调试信息。可以在config.json里，利用debug参数打开或关闭。<br/>
     *
     * @function Utils.log
     */
    static log() {
        if (Config.debug) {
            var msg = '';

            for (var i = 0; i < arguments.length; i++) {
                msg = msg + ' ' + arguments[i];
            }

            console.log(msg);
        }
    }

    /*
    *解析url中的参数
    *
    * @return {object} - 参数
    * */
    static getUrlParam (url){
            let json = {};
            let arr = url.substr(url.indexOf('?') + 1).split('&');
            arr.forEach(item=>{
                let tmp = item.split('=');
                json[tmp[0]] = tmp[1];
            });
            return json;
    }
}

Utils.Status = {
    initing: "0",
    completed:"1",
    starting:"2",
    connecting:"3",
    connectFalse:"4",
    recording:"5"
};
Utils.ConnectStatus = {
    initing:"0",
    connecting:"1",
    connectSuccess:"2",
    waitResult:"3",
    connectEnd:"4"
};
export default Utils;
